#pragma once
#include<iostream>
#include<mutex>
#include<memory>
using namespace std;

namespace Mango
{
template<class T>
class auto_ptr
{
public:
	auto_ptr(T* ptr = nullptr)
		:_ptr(ptr)
	{}
	~auto_ptr()
	{
		delete _ptr;
		_ptr = nullptr;
	}
	auto_ptr(auto_ptr<T>& p)
		:_ptr(p._ptr)
	{
		p._ptr = nullptr;//管理权转移
	}
	auto_ptr& operator=(auto_ptr<T>& p)
	{
		if (this != &p)
		{	
			delete _ptr;//释放原来的空间
			_ptr = p._ptr;
			p._ptr = nullptr;
		}
		return *this;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
//主要思想：管理权转移
private:
	T* _ptr;
};



//主要思想：防拷贝
template<class T>
class unique_ptr
{
public:
	unique_ptr(T* ptr = nullptr)
		:_ptr(ptr)
	{}
	~unique_ptr()
	{
		delete _ptr;
		_ptr = nullptr;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
	unique_ptr(const unique_ptr<T>& p) = delete;
	unique_ptr<T>& operator=(const unique_ptr<T>& p) = delete;
private:
	T* _ptr;
};

/*
//线程不安全版本
//主要思想：通过引用计数的方式，让多个对象管理同一块资源
//为了让管理不同资源的对象看到的是不同的引用计数->引用计数变量count在堆上开辟
//资源--引用计数绑定
template<class T>
class shared_ptr
{
public:
	shared_ptr(T* ptr = nullptr)
		:_ptr(ptr), pRefCount(new int(1))
	{}
	~shared_ptr()
	{
		if (--(*pRefCount) == 0)
		{
			cout << "delete: " << _ptr << endl;
			delete _ptr;
			_ptr = nullptr;
			delete pRefCount;
			pRefCount = nullptr;
		}
	}
	shared_ptr(shared_ptr<T>& p)
		:_ptr(p._ptr),pRefCount(p.pRefCount)
	{
		(*pRefCount)++;
	}
	shared_ptr<T>& operator=(shared_ptr<T>& p)
	{
		//不建议写成这样，因为如果二者指向的是同一块资源，但是不同的对象，仍然会进入if内部
		// 此时内部：先减少计数，再增加计数，多此一举！ 
		// 直接比较二者是否指向的是相同的资源更好
		//if (this != &p)
		if (_ptr != p._ptr)
		{
			//减少当前资源的计数
			if (--(*pRefCount) == 0)
			{
				//说明后序没有管理这块资源的对象了
				delete _ptr;
				delete pRefCount;
			}
			pRefCount = p.pRefCount;
			_ptr = p._ptr;
			(*pRefCount)++;
		}
		return *this;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
private:
	T* _ptr;
	int* pRefCount;
};

*/

//线程安全版本
//为了让管理相同资源的对象看到的是同一把互斥锁，让管理不同资源的对象看到的是不同的互斥锁，
//所以互斥锁对象也需要在堆上开辟
template<class T>
class shared_ptr
{
public:
	shared_ptr(T* ptr = nullptr)
		:_ptr(ptr), pRefCount(new int(1)), _mtx(new mutex)
	{}
	void ReleaseRef()
	{
		_mtx->lock();
		bool flag = false;
		if (--(*pRefCount) == 0)
		{
			flag = true;
			delete _ptr;
			delete pRefCount;
			_ptr = nullptr;
			pRefCount = nullptr;
			//注意：因为后序需要解锁，所以这里不能释放互斥锁，这里选择使用flag变量作为标记
		}
		_mtx->unlock();
		if (flag) delete _mtx;
	}
	void AddRef()
	{
		(*_mtx).lock();
		(*pRefCount)++;
		(*_mtx).unlock();
	}
	~shared_ptr()
	{
		ReleaseRef();
	}
	shared_ptr(shared_ptr<T>& p)
		:_ptr(p._ptr),pRefCount(p.pRefCount),_mtx(p._mtx)
	{
		AddRef();
	}
	shared_ptr<T>& operator=(shared_ptr<T>& p)
	{
		if (p._ptr != _ptr)
		{
			ReleaseRef();
			_ptr = p._ptr;
			_mtx = p._mtx;
			pRefCount = p.pRefCount;
			AddRef();
		}
		return *this;
	}
	int use_count()
	{
		return *pRefCount;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
private:
	T* _ptr;
	int* pRefCount;
	mutex* _mtx;
};

//week_ptr:不支持原生指针的构造方式,主要是用于解决shared_ptr的循环引用问题
//思想：参与资源的访问，但是不增加引用计数
template<class T>
class week_ptr
{
public:
	week_ptr() :_ptr(nullptr)
	{}
	week_ptr(const std::shared_ptr<T>& sp)
		:_ptr(sp.get())
	{}
	week_ptr<T>& operator=(const std::shared_ptr<T>& sp)
	{
		_ptr = sp.get();
		return *this;
	}
	~week_ptr()
	{
		delete _ptr;
		_ptr = nullptr;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
	T* get()
	{
		return _ptr;
	}
private:
	T* _ptr;
};
}